<template>
  <div v-if="visible" class="model-viewer-overlay" @click="handleOverlayClick">
    <div class="model-viewer-container" @click.stop>
      <div class="model-viewer-header">
        <h3>{{ modelName }}</h3>
        <button class="close-btn" @click="close">关闭模型</button>
      </div>
      <div class="model-viewer-content">
        <div ref="modelContainer" class="model-container"></div>
        <div v-if="loading" class="loading-overlay">
          <div class="loading-spinner"></div>
          <p>模型加载中...</p>
        </div>
        <div v-if="error" class="error-message">
          <p>{{ error }}</p>
          <button @click="retry">重试</button>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted, nextTick, watch } from "vue";
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

interface Props {
  visible: boolean;
  modelUrl: string;
  modelName: string;
  modelId: string;
}

interface Emits {
  (e: "update:visible", value: boolean): void;
  (e: "close"): void;
}

const props = defineProps<Props>();
const emit = defineEmits<Emits>();

const modelContainer = ref<HTMLElement | null>(null);
const loading = ref(false);
const error = ref("");

let modelInstance: any = null;
let scene: any = null;
let camera: any = null;
let renderer: any = null;
let controls: any = null;
let animationFrameId: number | null = null;
let sunLight: any = null;

/**
 * 加载Three.js库（已使用ES6模块导入，无需动态加载）
 */
const loadThreeJS = async () => {
  return Promise.resolve();
};

/**
 * 初始化Three.js场景
 */
const initThreeJS = async () => {
  if (!modelContainer.value) return;

  try {
    loading.value = true;
    error.value = "";

    // 等待DOM更新
    await nextTick();

    // 获取容器尺寸
    const container = modelContainer.value;
    const width = container.clientWidth;
    const height = container.clientHeight;

    // 创建场景
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0x87ceeb); // 使用天蓝色背景

    // 创建相机
    camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
    camera.position.set(5, 5, 5);

    // 创建渲染器
    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(width, height);
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.outputEncoding = THREE.sRGBEncoding; // 设置输出编码
    renderer.toneMapping = THREE.NoToneMapping; // 禁用色调映射，保持模型原始颜色
    renderer.toneMappingExposure = 0.8; // 标准曝光度，保持颜色真实
    renderer.shadowMap.enabled = true; // 启用阴影映射
    renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 使用柔和阴影
    container.appendChild(renderer.domElement);

    // 添加光源 - 优化模型颜色显示
    // 基础环境光 - 仅提供微弱照明，避免影响模型原色
    const ambientLight = new THREE.AmbientLight(0xffffff, 1.8); // 极低的环境光强度
    scene.add(ambientLight);

    // 主方向光 - 使用中性白光，仅用于产生阴影和基本照明
    sunLight = new THREE.DirectionalLight(0xffffff, 1.2); // 进一步降低强度，避免过亮
    sunLight.position.set(50, 50, 25); // 设置太阳位置 - 从斜上方照射
    sunLight.castShadow = true; // 启用阴影投射

    // 配置阴影参数 - 优化阴影质量
    sunLight.shadow.mapSize.width = 1024; // 降低阴影贴图分辨率，提高性能
    sunLight.shadow.mapSize.height = 1024;
    sunLight.shadow.camera.near = 0.5;
    sunLight.shadow.camera.far = 200;
    sunLight.shadow.camera.left = -50;
    sunLight.shadow.camera.right = 50;
    sunLight.shadow.camera.top = 50;
    sunLight.shadow.camera.bottom = -50;
    sunLight.shadow.bias = -0.001; // 调整阴影偏移

    scene.add(sunLight);

    // 添加补光 - 从下方提供柔和照明，减少阴影区域的黑暗
    const fillLight = new THREE.DirectionalLight(0xffffff, 0.3); // 柔和的补光
    fillLight.position.set(-30, -20, -30); // 从下方照射
    fillLight.castShadow = false; // 补光不投射阴影
    scene.add(fillLight);

    // 移除网格辅助线，让模型显示更干净

    // 加载GLTF模型
    await loadModel();

    // 添加轨道控制器
    controls = new OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;
    controls.dampingFactor = 0.05;

    // 开始渲染循环
    animate();
  } catch (err) {
    console.error("初始化Three.js场景失败:", err);
    error.value = "场景初始化失败";
  } finally {
    loading.value = false;
  }
};

/**
 * 加载GLTF模型
 */
const loadModel = async () => {
  if (!props.modelUrl || !props.modelId) {
    throw new Error("模型URL和ID不能为空");
  }

  try {
    // 使用GLTFLoader加载模型
    const loader = new GLTFLoader();

    return new Promise<void>((resolve, reject) => {
      loader.load(
        props.modelUrl,
        (gltf) => {
          const model = gltf.scene;
          modelInstance = model;

          // 启用模型的阴影并优化材质显示
          model.traverse((child) => {
            if (child.isMesh) {
              child.castShadow = true; // 投射阴影
              child.receiveShadow = false; // 模型不接收阴影，避免自阴影问题

              // 如果材质存在，保持材质原始属性，不做修改
              if (child.material) {
                // 保持材质原始颜色，不调整任何属性
                // 让模型显示真实的原始颜色
              }
            }
          });

          scene.add(model);

          // 调整相机位置以适应模型
          const box = new THREE.Box3().setFromObject(model);
          const center = box.getCenter(new THREE.Vector3());
          const size = box.getSize(new THREE.Vector3());

          const maxDim = Math.max(size.x, size.y, size.z);
          const fov = camera.fov * (Math.PI / 180);
          let cameraZ = Math.abs(maxDim / 2 / Math.tan(fov / 2));
          cameraZ *= 2; // 添加一些余量

          camera.position.set(
            center.x,
            center.y + cameraZ / 2,
            center.z + cameraZ
          );
          camera.lookAt(center);

          if (controls) {
            controls.target.copy(center);
            controls.update();
          }

          resolve();
        },
        (progress) => {
          console.log("模型加载进度:", progress);
        },
        (error) => {
          console.error("GLTF加载失败:", error);
          reject(new Error("模型加载失败"));
        }
      );
    });
  } catch (err) {
    console.error("加载模型失败:", err);
    throw new Error("模型加载失败");
  }
};

/**
 * 渲染循环
 */
const animate = () => {
  if (!renderer || !scene || !camera) return;

  animationFrameId = requestAnimationFrame(animate);

  if (controls) {
    controls.update();

    // 根据相机位置动态调整太阳位置，实现真实的日照角度变化
    if (sunLight && camera) {
      // 获取相机方向向量
      const cameraDirection = new THREE.Vector3();
      camera.getWorldDirection(cameraDirection);

      // 设置太阳位置为相机方向的反方向，模拟真实日照
      const sunDistance = 100; // 太阳距离
      const sunPosition = cameraDirection
        .clone()
        .multiplyScalar(-sunDistance)
        .add(camera.position);
      sunPosition.y = Math.max(sunPosition.y, 20); // 确保太阳在模型上方

      sunLight.position.copy(sunPosition);
      sunLight.target.position.set(0, 0, 0); // 指向场景中心
      sunLight.target.updateMatrixWorld();
    }
  }

  renderer.render(scene, camera);
};

/**
 * 关闭弹窗
 */
const close = () => {
  emit("update:visible", false);
  emit("close");
};

/**
 * 重试加载
 */
const retry = () => {
  error.value = "";
  initThreeJS();
};

/**
 * 处理遮罩层点击
 */
const handleOverlayClick = () => {
  close();
};

/**
 * 清理资源
 */
const cleanup = () => {
  // 取消动画帧
  if (animationFrameId !== null) {
    cancelAnimationFrame(animationFrameId);
    animationFrameId = null;
  }

  if (sunLight) {
    scene.remove(sunLight);
    sunLight = null;
  }

  if (modelInstance) {
    scene.remove(modelInstance);
    modelInstance = null;
  }

  if (renderer) {
    renderer.dispose();
    renderer = null;
  }

  if (camera) {
    camera = null;
  }

  if (scene) {
    scene.clear();
    scene = null;
  }

  if (controls) {
    controls.dispose();
    controls = null;
  }
};

// 监听模型URL变化
watch(
  () => props.modelUrl,
  () => {
    if (props.visible && props.modelUrl) {
      // 清理现有模型
      if (modelInstance) {
        scene.remove(modelInstance);
        modelInstance = null;
      }
      // 重新加载模型
      loadModel();
    }
  }
);

onMounted(() => {
  if (props.visible && props.modelUrl) {
    nextTick(() => {
      initThreeJS();
    });
  }
});

// 监听visible变化
watch(
  () => props.visible,
  (newVal) => {
    if (newVal && props.modelUrl) {
      nextTick(() => {
        initThreeJS();
      });
    } else {
      cleanup();
    }
  }
);

// 组件卸载时清理资源
onUnmounted(() => {
  cleanup();
});
</script>

<style scoped>
.model-viewer-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 9999;
}

.model-viewer-container {
  background: white;
  border-radius: 8px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
  width: 80%;
  max-width: 800px;
  height: 80%;
  max-height: 600px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.model-viewer-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 20px;
  border-bottom: 1px solid #e0e0e0;
  background: #f8f9fa;
}

.model-viewer-header h3 {
  margin: 0;
  font-size: 18px;
  color: #333;
}

.close-btn {
  background: none;
  border: none;
  font-size: 14px;
  cursor: pointer;
  color: #666;
  padding: 0;
  width: 60px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  transition: all 0.2s ease;
}

.model-viewer-content {
  flex: 1;
  position: relative;
  overflow: hidden;
}

.model-container {
  width: 100%;
  height: 100%;
  position: relative;
}

.loading-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(255, 255, 255, 0.9);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  z-index: 10;
}

.loading-spinner {
  width: 40px;
  height: 40px;
  border: 4px solid #f3f3f3;
  border-top: 4px solid #3498db;
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin-bottom: 16px;
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

.error-message {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(255, 255, 255, 0.95);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  z-index: 10;
  padding: 20px;
}

.error-message p {
  color: #e74c3c;
  margin-bottom: 16px;
  text-align: center;
}

.error-message button {
  background: #3498db;
  color: white;
  border: none;
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
  transition: background 0.2s ease;
}

.error-message button:hover {
  background: #2980b9;
}
</style>
